package com;

import com.bean.BeanA;

import com.beanpostprocessor.AutowiredPostProcessor;
import org.springframework.beans.*;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.lang.Nullable;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.stream.Collectors;

public class MyBeanFactory extends MyBeanRegistry {

    private final LinkedHashSet<RootBeanDefinition> beanDefinitions = new LinkedHashSet<RootBeanDefinition>();

    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
    private HashMap<Class<?>, String> classMappingBeanName = new HashMap<>();

    public MyBeanFactory() {

        beanPostProcessors.add(new AutowiredPostProcessor(this));
    }

    public void registerBean(Class<?> bean) {
        final RootBeanDefinition beanDef = new RootBeanDefinition(bean);
        beanDefinitions.add(beanDef);
        classMappingBeanName.put(bean, beanDef.getBeanClassName());
    }

    public void preInstantiateSingletons() {
        // 本来是要遍历 beanNames 来找到 BeanDefinition 然后再创建bean的
        // 这里省略了这一步骤
        for (RootBeanDefinition beanDefinition : beanDefinitions) {

            getBean(beanDefinition.getBeanClassName());
        }
    }

    public Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {


        return doCreateBean(beanName, mbd, args);
    }

    Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {

        // 1、创建 bean 实例
        final BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);

        final Object bean = instanceWrapper.getWrappedInstance();

        // 2、把刚刚创建的bean实例（尚未初始化完成）放入三级缓存
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

        // 3、填充 bean 的依赖 @Autowired
        populateBean(beanName, mbd, instanceWrapper);

        return bean;
    }

    void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
        // bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)

        PropertyValues pvs = mbd.getPropertyValues();

        for (InstantiationAwareBeanPostProcessor bp : instantiationAware()) {

            PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);

            // 做一些处理

            pvs = pvsToUse;
        }

        if (pvs != null) {
            applyPropertyValues(beanName, mbd, bw, pvs);
        }
    }

    void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {


    }

    List<InstantiationAwareBeanPostProcessor> instantiationAware() {
        return beanPostProcessors.stream().map(proc -> {
            if (proc instanceof InstantiationAwareBeanPostProcessor) {
                return (InstantiationAwareBeanPostProcessor) proc;
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 如果被循环依赖的对象有额外的aop代理，那么在解决循环依赖过程中，获取尚未初始化完成的被依赖bean
     * 那么这个bean的aop代理将会在这里被提前创建
     *
     * @param beanName
     * @param mbd
     * @param bean
     * @return
     */
    private Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {

        Object exposedObject = bean;
        for (SmartInstantiationAwareBeanPostProcessor smartInstantiationAwareBeanPostProcessor : smartPostProcessor()) {
            exposedObject = smartInstantiationAwareBeanPostProcessor.getEarlyBeanReference(exposedObject, beanName);
        }
        return exposedObject;
    }

    Collection<SmartInstantiationAwareBeanPostProcessor> smartPostProcessor() {
        return beanPostProcessors.stream().map(proc -> {
            if (proc instanceof SmartInstantiationAwareBeanPostProcessor) {
                return (SmartInstantiationAwareBeanPostProcessor) proc;
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanInstantiationException {

        // 通过 factoryMethod 创建

        // 通过 supplier 创建

        // 通过构造器创建

        // 等等创建方式被省略....

        // 使用默认的无参构造
        return instantiateBean(beanName, mbd);

    }


    BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {

        final Class<?> clazz = mbd.getBeanClass();
        Constructor<?> constructorToUse = null;
        try {
            constructorToUse = clazz.getDeclaredConstructor();
            final Object beanInstance = constructorToUse.newInstance();
            BeanWrapper bw = new BeanWrapperImpl(beanInstance);
            return bw;
        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            throw new BeanInstantiationException(clazz, e.getMessage(), e);
        }
    }

    public <T> T getBean(Class<T> clazz) {
        final String beanName = classMappingBeanName.get(clazz);
        return (T) getBean(beanName);
    }

    public Object getBean(String name) {
        return doGetBean(name, null, null, false);
    }

    <T> T doGetBean(
            String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
            throws BeansException {

        // 依次查询三级缓存，查看是否有bean
        Object sharedInstance = getSingleton(name);

        if (sharedInstance != null) {
            if (sharedInstance instanceof FactoryBean) {
                final FactoryBean<T> factoryBean = (FactoryBean<T>) sharedInstance;
                try {
                    return factoryBean.getObject();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            } else {
                return (T) sharedInstance;
            }
        } else {
            // 创建Bean
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(name);

            final Object singletonObject = createBean(name, mbd, null);

            addSingleton(name, singletonObject);
            return (T) singletonObject;
        }


    }

    private RootBeanDefinition getMergedLocalBeanDefinition(String name) {
        for (RootBeanDefinition beanDefinition : beanDefinitions) {
            if (Objects.equals(beanDefinition.getBeanClassName(), name)) {
                return beanDefinition;
            }
        }
        return null;
    }
}
